import WebSocket from 'ws';
const NODE_ENV = process.env.NODE_ENV

import store from './store';

import { getChatSessionUser, saveOrUpdateChatSessionUser, saveOrUpdateChatSessionUser4Init, updateContactName } from './db/models/ChatSessionUserModel'
import { saveChatMessage, saveChatMessageList4Init, updateChatMessage } from './db/models/ChatMessageModel'
import { updateContactNoReadCount } from './db/models/UserSettingModel';

let ws = null;

let wsUrl = null;
let sender = null;
let needReconnect = null;
let maxReconnectTimes = null;

let lockReconnect = false; // 加锁防止重复连接

/* 初始化ws连接的信息 */
const initWs = async (config, _sender) => {
    // 根据环境不同，选择不同的ws域名。使用token作为连接标识
    wsUrl = `${NODE_ENV !== 'development' ? store.getData("prodWsDomain") : store.getData("devWsDomain")}?token=${config.token}`;
    sender = _sender;
    needReconnect = true;
    maxReconnectTimes = 5;
    await createWs();
}

/* 建立ws连接 */
const createWs = () => {
    return new Promise((resolve, reject) => {
        ws = new WebSocket(wsUrl);
        ws.onopen = () => {
            // 发送心跳
            console.log("server connected");
            ws.send("heartbeat")
            // 连接成功后如果token不合法可能还会被服务器关闭，所以不能在这里重置重连次数
            // 如果在这里设置重连次数的话，假设被强制下线，token已经不合法了
            // 但是因为重连的时候连上了服务端（token校验在握手之后），重置了重连次数，虽然被服务端秒断，但是重连次数还是被重置了
            // 这导致了 连接成功重置次数->token校验失败被服务端断连->重新连接成功重置次数 的死循环
            // 所以改为在收到服务端发送的消息后才重置重连次数
        };

        ws.onmessage = async (e) => {
            maxReconnectTimes = 5;  // 每次连接成功后，重置重连次数
            console.log("receive message: ", e.data);
            // 处理消息
            const message = JSON.parse(e.data);
            const noticeType = message.noticeType;
            let data = { noticeType: noticeType };
            switch (noticeType) {
                case "WS_INIT":
                    // ws初始化连接，阻塞执行新增或更新操作
                    // 保存会话信息
                    await saveOrUpdateChatSessionUser4Init(message.chatSessionUserList);
                    // 保存消息信息
                    await saveChatMessageList4Init(message.chatMessageList);
                    // 更新申请信息数量
                    await updateContactNoReadCount(message.applyCount);
                    // 本地数据更新完成后才算ws创建好了
                    // 不然很可能主页面先加载好了，发送了获取会话列表的信号，获取的就不是这里保存的而是之前的了，导致会话列表没加载好
                    // 注册时可以测出来，打包后很可能注册完看不到机器人的消息，但是数据库里有，说明先查了数据库才收到WS_INIT通知更新数据库，顺序不对
                    resolve();
                    break;
                case "CONTACT_APPLY":  // 好友申请
                    // 申请信息数量+1
                    await updateContactNoReadCount(1)
                    data = Object.assign(data, message.userContactApply)
                    // 这里不直接把申请信息数量查完传过去是因为渲染进程有一个通用的获取申请数量的api，每次登陆也要获取没查看的申请数量，所以复用那个api就行了
                    sender.send("receiveMessage", data)  // 主要把类型传过去就行，知道是有申请信息就行
                    break;
                case "CONTACT_RENAME":  // 好友或群聊修改名字
                    // 修改会话消息
                    await updateContactName(message.contactId, message.contactName)
                    sender.send("receiveMessage", message)  // 整个传过去
                    break;
                /* 下面这类都是展示到聊天界面的内容 */
                case "CHAT":  // 聊天消息
                case "USER_ADD_ACCEPT":  // 自己同意别人的好友申请
                case "USER_ADD_BY_OTHERS":  // 别人同意添加自己为好友
                case "GROUP_CREATED":  // 群聊创建成功
                case "GROUP_DISBAND":  // 群聊解散
                case "GROUP_ADD_ACCEPTED":  // 加群申请被接受
                case "GROUP_MEMBER_LEAVE_OR_IS_REMOVED":  // 群成员退群或被踢出
                    data = Object.assign(data, message.chatDataVO)
                    // 通知类消息没有sendUserId
                    if (data.sendUserId != null && data.sendUserId == store.getUserId() && data.contactType == "GROUP") {
                        // 群聊的消息服务端会每个人都发一份，包括发送者自己，所以要过滤掉
                        return;
                    }
                    if (noticeType == "GROUP_MEMBER_LEAVE_OR_IS_REMOVED" && message.leaveUserId == store.getUserId()) {
                        // 自己离开群聊，不需要给自己发消息
                        return;
                    }
                    // 保存到session和message表
                    await saveChatMessage(Object.assign({}, data))
                    let noReadCount = await saveOrUpdateChatSessionUser(store.getUserData("currentSessionId"), Object.assign({}, data))
                    // 补充未读消息数量
                    data.noReadCount = noReadCount
                    // 将信息返回给渲染进程
                    sender.send("receiveMessage", data)
                    break;
                case "FILE_UPLOAD_COMPLETED":
                    // 对方发送的文件上传完成，可以开始下载
                    data = Object.assign(data, message.chatMessage)
                    // 更新消息状态
                    await updateChatMessage({ sendStatus: 1 }, { messageId: data.messageId })
                    // 将信息返回给渲染进程
                    sender.send("receiveMessage", data)
                    break;
                case "FORCE_OFFLINE":  // 强制下线
                    sender.send("receiveMessage", data)
                    break;
            }
        };

        ws.onclose = () => {
            // 有时候断开不一定是主动关闭的，所以需要重连
            console.log("onclose")
            if (interval != null) {
                clearInterval(interval);  // 清除定时器，不然重连之后会发多个心跳
            }
            reconnect();
        };

        ws.onerror = () => {
            console.log("onerror")
            reconnect();
        };

        const reconnect = () => {
            if (!needReconnect) {
                console.log("disconnected and no need to reconnect")
                return;
            }
            if (ws != null) {
                ws.close();
                ws = null;
                console.log("disconnected")
            }
            // 加锁防止重复连接
            if (lockReconnect) {
                return;
            }
            lockReconnect = true;
            if (maxReconnectTimes > 0) {
                console.log("try to reconnect, " + maxReconnectTimes + " times left")
                maxReconnectTimes--;
                setTimeout(() => {
                    createWs();
                    lockReconnect = false;
                }, 5000)
            } else {
                console.log("reach max reconnect times, no need to reconnect")
            }
        }

        // 不断发送心跳
        const interval = setInterval(() => {
            if (ws != null && ws.readyState == 1) {
                process.stdout.write(".");  // 不换行输出发送心跳的标志
                ws.send('heartbeat')
            } else {
                reconnect();
            }
        }, 5000);
    })
}

/* 断开ws连接 */
const closeWs = () => {
    needReconnect = false;
    if (ws != null) {
        ws.close();
    }
}


export {
    initWs,
    closeWs  // 关闭ws连接由外部调用
}